<?php
/**
 * App          系统核心
 * @copyright   Copyright(c) 2024-2034
 * @author      tianjie <527707665@qq.com>
 * @link        http://www.tinphp.com/
 * @version     4.1
 */

namespace tian;

use tian\exception\HttpException;
use tian\exception\HttpResponseException;

class App
{
    const VERSION = '4.1.12';

    public $debug = false;

    protected $tianPath = '';//框架目录
    protected $rootPath = '';//应用根目录
    protected $appPath = '';//应用业务目录
    protected $configPath = '';//应用配置目录
    protected $runtimePath = '';//缓存日志目录
    protected $viewPath = '';//视图目录

    public static $config = [];
    public $objConfig;

    public static $envData = [];
    public $env;
    public $middleware;

    public $is_check_app_middleware = true; //是否需要验证应用中间件

    /**
     * 架构方法
     * @access public
     * @param string $rootPath 应用根目录
     */
    public function __construct(string $rootPath = '')
    {
        $this->tianPath = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
        $this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
        $this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
        $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
        $this->viewPath = $this->rootPath . 'view' . DIRECTORY_SEPARATOR;

        $this->objConfig = new Config();
        $this->env = new Env();
        $this->middleware = new Middleware();
    }

    /**
     * 获取应用根目录
     * @access protected
     * @return string
     */
    protected function getDefaultRootPath(): string
    {
        return dirname($this->tianPath, 4) . DIRECTORY_SEPARATOR;
    }

    /**
     * 获取框架版本
     * @return string
     */
    public static function version(): string
    {
        return static::VERSION;
    }

    /**
     * 获取框架运行模式
     * @return bool
     */
    public function debug(): bool
    {
        return $this->debug;
    }

    /**
     * 应用根目录
     * @access public
     * @return string
     */
    public function getRootPath(): string
    {
        return $this->rootPath;
    }

    /**
     * 应用业务目录
     * @access public
     * @return string
     */
    public function getAppPath(): string
    {
        return $this->appPath;
    }

    /**
     * 应用配置目录
     * @access public
     * @return string
     */
    public function getConfigPath(): string
    {
        return $this->configPath;
    }

    /**
     * 缓存日志目录
     * @access public
     * @return string
     */
    public function getRuntimePath(): string
    {
        return $this->runtimePath;
    }

    /**
     * 视图目录
     * @access public
     * @return string
     */
    public function getViewPath(): string
    {
        return $this->viewPath;
    }

    /**
     * 执行应用程序
     * @param Request|null $request
     * @return Response|response\Json
     */
    public function run(Request $request = null)
    {
        //创建request对象
        $request = is_null($request) ? Request::instance() : $request;
        try {
            //载入环境变量
            $this->loadEnv();

            //初始化载入系统预设置
            $this->initSytem();

            //初始化载入应用配置信息
            $this->initApp();

            //应用模式设置
            $this->appModeInit();

            //设置时区
            date_default_timezone_set($this->objConfig->get('config.default_timezone', 'Asia/Shanghai'));

            //注册事件(待开发)

            //加载中间件(待开发)[全局中间件+应用中间件]
            $this->loadMiddleware($request);

            //解析路由
            $route = $this->route($request);

            //记录当前调度信息
            $dispatch = $request->dispatch($route);

            //设置当前模块+控制器+方法+类命名空间
            $this->setRequest($request, $dispatch);

            //路由分发 执行
            $data = $this->implement($request, $dispatch);
        } catch (HttpResponseException $exception) {
            $data = $exception->getResponse();
        }

        // 输出数据到客户端
        if ($data instanceof Response) {
            $response = $data;
        } elseif (!is_null($data)) {
            // 默认自动识别响应输出类型
            $config = $this->objConfig->get();
            $type = $request->isAjax() ? $config['config']['default_ajax_return'] : $config['config']['default_return_type'];

            $response = Response::create($data, $type);
        } else {
            $response = Response::create();
        }
        return $response;
    }

    /**
     * 载入环境变量
     * @param string $file
     * @param string $name
     * @return void
     */
    private function loadEnv(string $file = '', string $name = '')
    {
        $file = $file == '' ? $this->getRootPath() . $name . '.env' : $file;
        if (is_file($file)) {
            $this->env->load($file);
            self::$envData = $this->env->get();
        }
    }

    /**
     * 初始化载入系统预设置
     * @return void
     */
    private function initSytem()
    {
        $file = $this->tianPath . 'configure.php';
        $this->objConfig->load($file);
    }

    /**
     * 初始化载入应用配置信息
     * @return void
     */
    private function initApp()
    {
        $configPath = $this->configPath;
        $config = $this->objConfig;

        //载入公共函数文件
        $functionFile = $this->appPath . "function.php";
        if (is_file($functionFile)) {
            include_once($functionFile);
        }

        //初始化助手函数
        include_once($this->tianPath . "helper.php");

        //获取应用配置目录文件
        $configFiles = [];
        if (is_dir($configPath)) {
            $configFiles = glob($configPath . '*' . '.php');
        }
        if (!empty($configFiles)) {
            foreach ($configFiles as $key => $configFile) {
                //载入应用配置
                $range = pathinfo($configFile, PATHINFO_FILENAME);
                $config->load($configFile, '', $range);
            }
        }

        self::$config = $config->get();
    }

    /**
     * 应用模式设置
     * @return void
     */
    private function appModeInit()
    {
        if (!$this->debug) {
            $cfg = self::$config;
            $this->debug = !empty($cfg['config']['app_debug']) ? $cfg['config']['app_debug'] : (bool)$this->env->get('APP_DEBUG');

            //关闭错误信息
            ini_set('display_errors', 'Off');
        }

        if (!runningInConsole()) {
            if (ob_get_level() > 0) {
                $output = ob_get_clean();
            }
            ob_start();
            if (!empty($output)) {
                echo $output;
            }
        }
    }

    /**
     * 载入中间件
     * @param $request
     * @return void
     */
    private function loadMiddleware($request)
    {
        $config = self::$config;
        $middlewareAry = $config['middleware'];

        $this->middleware::load($middlewareAry);
    }

    /**
     * 解析路由
     * @param $request
     * @return array|false|mixed
     */
    private function route($request)
    {
        $path = $request->path();
        $depr = config('config.pathinfo_depr');

        //载入应用路由
        $files = $this->configPath . 'route.php';
        if (file_exists($files)) {
            include $files;
        }

        //路由检测
        $result = Route::check($request, $path, $depr);

        //路由无效 自动解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2...
        if (false === $result) {
            $result = Route::parseUrl($path, $depr, config('config.controller_auto_search'));
        }

        return $result;
    }

    /**
     * 设置当前模块+控制器+方法+类命名空间
     * @param $request
     * @param $dispatch
     * @return void
     */
    private function setRequest($request, $dispatch)
    {
        $moduleAry = $dispatch['module'];
        if (is_string($moduleAry)) {
            $moduleAry = explode('/', $moduleAry);
        }

        $is_cli = PHP_SAPI == 'cli';
        if (!$is_cli) {
            $module = strip_tags(strtolower($moduleAry[0] ?: config('config.default_module')));
            $controller = strip_tags(strtolower($moduleAry[1] ?: config('config.default_controller')));
            $action = strip_tags(strtolower($moduleAry[2] ?: config('config.default_action')));
        } else {
            $module = strip_tags($moduleAry[0] ?: config('config.default_module'));
            $controller = strip_tags($moduleAry[1] ?: config('config.default_controller'));
            $action = strip_tags($moduleAry[2] ?: config('config.default_action'));
        }

        //设置当前的模块名
        $request->module($module);
        //设置当前的控制器名
        $request->controller($controller);
        //设置当前的方法名
        $request->action($action);

        //设置或者获取当前的类命名空间
        $controllerClass = 'app\\' . $module . '\\controller\\' . ucfirst($controller);
        $request->controllerClass($controllerClass);
    }

    /**
     * 路由分发
     * @param $request
     * @param $dispatch
     * @return mixed|Response|response\Json|null
     */
    private function implement($request, $dispatch)
    {
        $type = $dispatch['type'] ?? '';
        switch ($type) {
            case 'redirect': // 重定向跳转
                $data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']);
                break;
            case 'module': // 模块/控制器/操作
                //验证全局中间件
                $this->is_check_app_middleware = true;
                $middlewares = [];
                $instances = $this->middleware::getMiddleware();
                if (!empty($instances)) {
                    foreach ($instances as $item) {
                        $middlewareClass = $item[0];
                        $middleware = new $middlewareClass();
                        if ($middleware instanceof MiddlewareInterface) {
                            $middlewares [] = $middlewareClass;
                        }
                    }
                    $resMid = $this->handleMiddleware($request, $instances, $middlewares);
                    return $resMid;
                }

                $data = $this->exec();
                break;
            case 'response': // Response 实例
                $data = $dispatch['response'];
                break;
            default:
                throw new \InvalidArgumentException('dispatch type not support');
        }
        return $data;
    }

    /**
     * 中间件流程控制
     * @param $request
     * @param $instances
     * @param $middlewares
     * @return Response|void
     */
    private function handleMiddleware($request, $instances, $middlewares)
    {
        if ($this->debug) {
            setLog('中间件流程-开始：' . json_encode($instances), 'debug');
        }

        // 模拟核心处理器
        $coreHandler = function ($request) {
            if ($this->debug) {
                setLog('中间件流程-完成-核心处理器执行[执行业务]', 'debug');
            }
            //return new Response('OK', 200);
            return $this->exec();
        };
        //构建模拟中间件类
        $pipeline = array_reduce(
            array_reverse($middlewares),
            function ($stack, $middleware) {
                return function ($request) use ($stack, $middleware) {
                    if ($this->debug) {
                        setLog('中间件流程-执行-[' . $middleware . ']', 'debug');
                    }
                    return (new $middleware)->handle($request, $stack);
                };
            },
            $coreHandler
        );
        $resMid = $pipeline($request); // 注意这里传入的是$request而不是$middlewares
        if (is_object($resMid) && ($resMid instanceof Response)) {
            //阻断返回-中间件中止请求
            if ($this->debug) {
                setLog('中间件流程-阻断返回-中间件中止请求。[class:' . get_class($resMid) . '][' . json_encode($resMid->getContent(),JSON_UNESCAPED_UNICODE) . ']', 'debug');
            }
            return $resMid;
        }
    }

    /**
     * 执行业务
     * @return Response|void|null
     */
    private function exec()
    {
        $module = request()->module();
        $controller = request()->controller();
        $action = request()->action();
        $controllerClass = request()->controllerClass(true);

        $mvcController = $this->appPath . $module . "/controller";
        $mvcFileName = strtolower($controller) . '.php';

        //验证控制器文件是否存在
        $isExistence = false;
        $controllerAryList = get_filenamesbydir($mvcController);
        if (!empty($controllerAryList)) {
            foreach ($controllerAryList as $item) {
                if (strtolower($item['name']) == $mvcFileName) {
                    $isExistence = true;
                }
            }
        }

        //检测控制器文件是否存在
        if ($isExistence) {
            //验证应用中间件
            if($this->is_check_app_middleware){
                $request = request();
                $this->is_check_app_middleware = false;
                $middlewares = [];
                $instances = $this->middleware::getMiddleware($module);
                if (!empty($instances)) {
                    foreach ($instances as $item) {
                        $middlewareClass = $item[0];
                        $middleware = new $middlewareClass();
                        if ($middleware instanceof MiddlewareInterface) {
                            $middlewares [] = $middlewareClass;
                        }
                    }
                    return $this->handleMiddleware($request, $instances, $middlewares);
                }
            }

            //实例化数据库操作类
            new Db();

            //实例化当前应用控制器
            try {
                $myfC = new $controllerClass();

                if (is_object($myfC) && method_exists($myfC, $action)) {
                    //初始化系统方法 模板引擎
                    $myfC->_sys_init_action($module, $controller);
                    //执行前置方法
                    $myfC->_before_action();
                    //执行用户方法
                    $data = $myfC->{$action}();
                    //执行后置方法
                    $myfC->_after_action();

                    return $data;
                } else {
                    header('Content-Type:application/json; charset=utf-8');
                    exit("The method " . $action . " () not found in class " . ucfirst($controller));
                }
            } catch (Exception $e) {
                header('Content-Type:application/json; charset=utf-8');
                exit('class not exists: "' . $controllerClass . '" 。error: ' . $e->getMessage());
            }

        } else {
            header('Content-Type:application/json; charset=utf-8');
            exit("404 The page not found：" . $mvcController . '/' . $mvcFileName);
        }
    }

    /**
     * @param string $name
     * @return mixed|void|null
     */
    public static function getConfig($name)
    {
        if (!strpos($name, '.')) {
            return self::$config[$name] ?? null;
        }

        // 二维数组设置和获取支持
        $name = explode('.', $name);
        $obj = [];
        foreach ($name as $item) {
            if (empty($obj)) {
                $obj = self::$config[$item];
            } else {
                if (is_array($obj[$item])) {
                    $obj = $obj[$item];
                } else {
                    return $obj[$item];
                }
            }
        }

        return null;
    }

    /**
     * 获取环境变量值
     * @param string $name
     * @param string|null $default
     * @return mixed|string|null
     */
    public static function getEnv(string $name, string $default = null)
    {
        return self::$envData[$name] ?: $default;
    }

}